package com.xuecheng.media.service.jobhandler;

import com.xuecheng.base.utils.Mp4VideoUtil;
import com.xuecheng.media.model.po.MediaProcess;
import com.xuecheng.media.service.MediaFileProcessService;
import com.xuecheng.media.service.MediaFileService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * ClassName: VideoTask
 * Package: com.xuecheng.media.service.jobhandler
 * Description: 视频处理任务
 *
 * @Author Endeavor-ZQB
 * @Create 2023/2/8 17:54
 * @Version 1.0
 */
@Slf4j
@Component
public class VideoTask {

    @Autowired
    MediaFileProcessService mediaFileProcessService;

    @Autowired
    MediaFileService mediaFileService;

    //获取ffmpeg绝对路径
    @Value("${videoprocess.ffmpegpath}")
    String ffmpegpath;

    /**
     * 视频处理
     */
    @XxlJob("videoJobHandler")
    public void shardingJobHandler() throws Exception {

        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();

        XxlJobHelper.log("分片参数：当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);

        log.info("分片参数：当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);
        log.info("开始执行第"+shardIndex+"批任务");

        //查询待处理的任务，一次处理的任务数和CPU核心数一致
        List<MediaProcess> mediaProcessList = mediaFileProcessService.getMediaProcessList(shardTotal, shardIndex, 2);

        if (mediaProcessList==null || mediaProcessList.size()<=0){
            log.debug("查询到待处理的任务为0");
            return;
        }

        //要处理的任务数
        int size = mediaProcessList.size();

        log.debug("取出待处理视频记录"+size+"条");
        if(size<=0){
            return ;
        }

        //启动size数量的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(size);

        //计数器
        CountDownLatch countDownLatch = new CountDownLatch(size);

        //遍历mediaProcessList，将任务放入线程池中
        mediaProcessList.forEach(mediaProcess -> {
            threadPool.execute(()->{
                //任务的执行逻辑
                //桶
                String bucket = mediaProcess.getBucket();
                //存储路径
                String filePath = mediaProcess.getFilePath();
                //原始视频的md5值
                String fileId = mediaProcess.getFileId();

                //视频处理的状态
                String status = mediaProcess.getStatus();
                //幂等性
                if ("2".equals(status)){
                    log.debug("视频已经处理，不用再次处理，视频信息:{}",mediaProcess);
                    countDownLatch.countDown();  //计数器减1
                    return;
                }

                //下载原始视频到本地
                //先创建临时文件，为原始的视频文件
                File originalVideo = null;
                //处理结束的MP4文件
                File mp4Video =null;

                try {
                    originalVideo = File.createTempFile("original",null);
                    mp4Video = File.createTempFile("mp4",".mp4");
                } catch (IOException e) {
                    log.error("下载待处理的原始文件前创建临时文件失败");
                    countDownLatch.countDown();  //计数器减1
                    return;
                }

                try {
                    originalVideo = mediaFileService.downloadFileFromMinIO(originalVideo,bucket,filePath);

                    //使用工具类将avi视频转码为MP4视频
                    Mp4VideoUtil mp4VideoUtil = new Mp4VideoUtil(ffmpegpath,originalVideo.getAbsolutePath(),mp4Video.getName(),mp4Video.getAbsolutePath());
                    String result = mp4VideoUtil.generateMp4();
                    if (!result.equals("success")){
                        //记录错误信息
                        log.error("generateMp4 error ,video_path is:{},error msg is:{}",bucket+filePath,result);
                        mediaFileProcessService.saveProcessFinishStatus(3,fileId,null,result);
                        countDownLatch.countDown();  //计数器减1
                        return;
                    }

                    //上传MP4视频到MINIO
                    //文件路径
                    String objectName = getFilePathByMd5(fileId, ".mp4");
                    mediaFileService.addMediaFilesToMinIO(mp4Video.getAbsolutePath(),bucket,objectName);

                    //设置URL
                    String url = "/" + bucket +"/"+ objectName;
                    //将url存储至数据，并更新状态为成功，并将待处理视频记录删除存入历史

                    mediaFileProcessService.saveProcessFinishStatus(2,fileId,url,null);
                } catch (Exception e) {
                    e.printStackTrace();
                    countDownLatch.countDown();  //计数器减1
                    return;
                } finally {
                    //清理文件
                    if (originalVideo!=null){
                        try {
                            originalVideo.delete();
                        } catch (Exception e) {
                            e.printStackTrace();
                            countDownLatch.countDown();  //计数器减1
                            return;
                        }
                    }
                    if (mp4Video!=null){
                        try {
                            mp4Video.delete();
                        } catch (Exception e) {
                            e.printStackTrace();
                            countDownLatch.countDown();  //计数器减1
                            return;
                        }
                    }
                }
                countDownLatch.countDown();  //计数器减1

            });
        });

        //等待,给一个充裕的超时时间,防止无限等待，到达超时时间还没有处理完成则结束任务(countDownLatch计数器为0，结束)
        countDownLatch.await(30, TimeUnit.MINUTES);


    }

    private String getFilePathByMd5(String fileMd5,String fileExt){
        return   fileMd5.substring(0,1) + "/" + fileMd5.substring(1,2) + "/" + fileMd5 + "/" +fileMd5 +fileExt;
    }
}
